/// Token 数据模型
/// 
/// 用于封装登录成功后返回的令牌信息
class TokenModel {
  /// 访问令牌
  final String accessToken;
  
  /// 令牌过期时间（单位：秒）
  final int expiresIn;
  
  /// 刷新令牌
  final String refreshToken;
  
  /// 刷新令牌过期时间（单位：秒）
  final int refreshExpiresIn;
  
  /// 令牌获取时间（时间戳，毫秒）
  final int obtainedAt;

  const TokenModel({
    required this.accessToken,
    required this.expiresIn,
    required this.refreshToken,
    required this.refreshExpiresIn,
    required this.obtainedAt,
  });
  
  /// 创建新的 Token 实例，自动设置获取时间
  factory TokenModel.create({
    required String accessToken,
    required int expiresIn,
    required String refreshToken,
    required int refreshExpiresIn,
  }) {
    return TokenModel(
      accessToken: accessToken,
      expiresIn: expiresIn,
      refreshToken: refreshToken,
      refreshExpiresIn: refreshExpiresIn,
      obtainedAt: DateTime.now().millisecondsSinceEpoch,
    );
  }

  /// 从 JSON 创建 Token 数据对象
  factory TokenModel.fromJson(Map<String, dynamic> json) {
    // 如果 JSON 中没有 obtained_at，使用当前时间（适用于服务器响应）
    final obtainedAt = json['obtained_at']?.toInt() ?? DateTime.now().millisecondsSinceEpoch;
    
    return TokenModel(
      accessToken: json['access_token']?.toString() ?? '',
      expiresIn: json['expires_in']?.toInt() ?? 0,
      refreshToken: json['refresh_token']?.toString() ?? '',
      refreshExpiresIn: json['refresh_expires_in']?.toInt() ?? 0,
      obtainedAt: obtainedAt,
    );
  }

  /// 转换为 JSON 格式
  Map<String, dynamic> toJson() {
    return {
      'access_token': accessToken,
      'expires_in': expiresIn,
      'refresh_token': refreshToken,
      'refresh_expires_in': refreshExpiresIn,
      'obtained_at': obtainedAt,
    };
  }

  /// 创建副本，支持部分字段更新
  TokenModel copyWith({
    String? accessToken,
    int? expiresIn,
    String? refreshToken,
    int? refreshExpiresIn,
    int? obtainedAt,
  }) {
    return TokenModel(
      accessToken: accessToken ?? this.accessToken,
      expiresIn: expiresIn ?? this.expiresIn,
      refreshToken: refreshToken ?? this.refreshToken,
      refreshExpiresIn: refreshExpiresIn ?? this.refreshExpiresIn,
      obtainedAt: obtainedAt ?? this.obtainedAt,
    );
  }

  /// 判断令牌是否有效
  bool get isValid => accessToken.isNotEmpty && expiresIn > 0;
  
  /// 判断刷新令牌是否有效
  bool get isRefreshValid => refreshToken.isNotEmpty && refreshExpiresIn > 0;

  /// 获取令牌过期时间（DateTime）
  DateTime get expiresAt {
    if (obtainedAt == 0) {
      // 如果没有获取时间，使用当前时间 + expiresIn（向后兼容）
      return DateTime.now().add(Duration(seconds: expiresIn));
    }
    // 基于获取时间计算准确的过期时间
    return DateTime.fromMillisecondsSinceEpoch(obtainedAt).add(Duration(seconds: expiresIn));
  }
  
  /// 获取刷新令牌过期时间（DateTime）
  DateTime get refreshExpiresAt {
    if (obtainedAt == 0) {
      // 如果没有获取时间，使用当前时间 + refreshExpiresIn（向后兼容）
      return DateTime.now().add(Duration(seconds: refreshExpiresIn));
    }
    // 基于获取时间计算准确的过期时间
    return DateTime.fromMillisecondsSinceEpoch(obtainedAt).add(Duration(seconds: refreshExpiresIn));
  }
  
  /// 获取访问令牌剩余有效时间（秒）
  int get remainingExpiresIn {
    final now = DateTime.now();
    final expiryTime = expiresAt;
    if (now.isAfter(expiryTime)) {
      return 0; // 已过期
    }
    return expiryTime.difference(now).inSeconds;
  }
  
  /// 获取刷新令牌剩余有效时间（秒）
  int get remainingRefreshExpiresIn {
    final now = DateTime.now();
    final expiryTime = refreshExpiresAt;
    if (now.isAfter(expiryTime)) {
      return 0; // 已过期
    }
    return expiryTime.difference(now).inSeconds;
  }

  /// 判断访问令牌是否已过期
  bool get isExpired {
    return DateTime.now().isAfter(expiresAt);
  }
  
  /// 判断刷新令牌是否已过期
  bool get isRefreshExpired {
    return DateTime.now().isAfter(refreshExpiresAt);
  }

  /// 判断访问令牌是否即将过期（少于指定时间）
  bool isExpiringSoon([Duration threshold = const Duration(minutes: 1)]) {
    final now = DateTime.now();
    final expiryTime = expiresAt;
    return expiryTime.difference(now) <= threshold;
  }
  
  /// 判断刷新令牌是否即将过期（少于1天）
  bool get isRefreshExpiringSoon {
    return remainingRefreshExpiresIn < 86400; // 86400秒 = 1天
  }

  /// 获取令牌剩余有效期的友好显示
  String get expiresInText {
    final remaining = remainingExpiresIn;
    if (remaining <= 0) return '已过期';
    
    final Duration duration = Duration(seconds: remaining);
    final int days = duration.inDays;
    final int hours = duration.inHours.remainder(24);
    final int minutes = duration.inMinutes.remainder(60);
    
    if (days > 0) {
      return '剩余${days}天${hours}小时';
    } else if (hours > 0) {
      return '剩余${hours}小时${minutes}分钟';
    } else if (minutes > 0) {
      return '剩余${minutes}分钟';
    } else {
      return '剩余${duration.inSeconds}秒';
    }
  }
  
  /// 获取令牌过期时间的友好显示
  String get expiresAtText {
    final expiryTime = expiresAt;
    final now = DateTime.now();
    
    if (now.isAfter(expiryTime)) {
      return '已于 ${_formatDateTime(expiryTime)} 过期';
    } else {
      return '将于 ${_formatDateTime(expiryTime)} 过期';
    }
  }
  
  /// 格式化时间显示
  String _formatDateTime(DateTime dateTime) {
    return '${dateTime.year}-${dateTime.month.toString().padLeft(2, '0')}-${dateTime.day.toString().padLeft(2, '0')} '
           '${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}';
  }

  /// 获取 Bearer 格式的授权头
  String get bearerToken => 'Bearer $accessToken';

  @override
  String toString() {
    // 为了安全，只显示 token 的前8位和后4位
    final maskedToken = accessToken.length > 12
        ? '${accessToken.substring(0, 8)}...${accessToken.substring(accessToken.length - 4)}'
        : '****';
    return 'TokenModel(accessToken: $maskedToken, expiresIn: $expiresIn)';
  }

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) return true;
    return other is TokenModel &&
        other.accessToken == accessToken &&
        other.expiresIn == expiresIn &&
        other.refreshToken == refreshToken &&
        other.refreshExpiresIn == refreshExpiresIn &&
        other.obtainedAt == obtainedAt;
  }

  @override
  int get hashCode => 
      accessToken.hashCode ^ 
      expiresIn.hashCode ^ 
      refreshToken.hashCode ^ 
      refreshExpiresIn.hashCode ^
      obtainedAt.hashCode;
}
